package com.hsl.network.net;

import java.net.BindException;
import java.util.List;

import javax.swing.JOptionPane;

import com.hsl.network.listener.MessageReceivedListener;
import com.hsl.network.model.DataManager;
import com.hsl.network.net.handler.TCPHandler;
import com.hsl.network.ui.select.ChannelOption;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.MessageToByteEncoder;

public class NetTCPServer extends AbstractNetTcp {
    private ServerBootstrap server;
    protected DataManager data;
    protected MessageReceivedListener listener;

    public NetTCPServer(DataManager data, MessageReceivedListener listener) {
        super(data, listener, "TCP Server");
        this.data = data;
        this.listener = listener;
    }

    @Override
    public boolean start(String ip, int port, int headLength) {
        try {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workGroup = new NioEventLoopGroup();
            server = new ServerBootstrap();
            server.group(bossGroup, workGroup);
            server.channel(NioServerSocketChannel.class);
            server.option(io.netty.channel.ChannelOption.SO_BACKLOG, 1024);
            server.option(io.netty.channel.ChannelOption.SO_SNDBUF, 32 * 1024);
            server.option(io.netty.channel.ChannelOption.SO_RCVBUF, 32 * 1024);
            server.option(io.netty.channel.ChannelOption.SO_KEEPALIVE, true);
            server.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    if (headLength == 0){
                        ch.pipeline().addLast("encoder", new MessageToByteEncoder<byte[]>() {
                            @Override
                            protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception {
                                out.writeBytes(msg);
                            }
                        });
                    }else {
                        ch.pipeline().addLast("frameLengthDecoder", new LengthFieldBasedFrameDecoder(
                                65535, 0,
                                headLength, 0, headLength));
                        ch.pipeline().addLast("encoder", new LengthFieldPrepender(headLength));
                    }
                    ch.pipeline().addLast("handler", new TCPHandler(data, listener));
                }
            });
            ChannelFuture f = server.bind(ip, port).sync();
            channel = f.channel();
        } catch (InterruptedException e) {
            JOptionPane.showMessageDialog(null, "NetTCPServer服务器异常", "NetTCPServer服务器异常", JOptionPane.OK_OPTION);
            return false;
        } catch (Exception e) {
            if (e instanceof BindException) {
                JOptionPane.showMessageDialog(null, "端口[" + port + "]已经被占用", "参数异常", JOptionPane.OK_OPTION);
            }
            return false;
        }
        return true;
    }

    @Override
    public void sendMsg(String text) {
        List<Object> channels = data.getConnections();
        byte[] bytes = msg2Bytes(text);
        if (bytes != null) {
            int limit = bytes.length;
            ByteBuf byteBuf = Unpooled.buffer(limit);
            byteBuf.writeBytes(bytes);
            for (Object obj : channels) {
                if (obj instanceof ChannelOption) {
                    ChannelOption c = (ChannelOption) obj;
                    c.getChannel().writeAndFlush(byteBuf);
                } else {
                    System.err.println("TCP 连接列表中有非TCP连接" + obj);
                }
            }
        }

    }

    @Override
    public boolean isServer() {
        return true;
    }
}